#include "GraphicDevice.h"

namespace egg{

    GraphicDevice* gDevice=0;

    GraphicDeviceInfo::GraphicDeviceInfo() : 
    redBits(0), greenBits(0), blueBits(0), alphaBits(0), depthBits(0),
        stencilBits(0), maxTextureSize(0), maxLights(0), maxAttribStacks(0),
        maxModelViewStacks(0), maxClipPlanes(0), maxTextureStacks(0) {
            char* str = 0;
            char* tok = 0;
            vendor = (char*) glGetString(GL_VENDOR);
            renderer = (char*) glGetString(GL_RENDERER);
            version = (char*) glGetString(GL_VERSION);
            glslVersion = (char*) glGetString(GL_SHADING_LANGUAGE_VERSION);

            str = (char*) glGetString(GL_EXTENSIONS);
            if (str) {
                tok = strtok((char*) str, " ");
                while (tok) {
                    extensions.push_back(tok);
                    tok = strtok(0, " ");
                }
            }
            std::sort(extensions.begin(), extensions.end());

            glGetIntegerv(GL_RED_BITS, &redBits);
            glGetIntegerv(GL_GREEN_BITS, &greenBits);
            glGetIntegerv(GL_BLUE_BITS, &blueBits);
            glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
            glGetIntegerv(GL_DEPTH_BITS, &depthBits);
            glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
            glGetIntegerv(GL_MAX_LIGHTS, &maxLights);
            glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
            glGetIntegerv(GL_MAX_CLIP_PLANES, &maxClipPlanes);
            glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &maxModelViewStacks);
            glGetIntegerv(GL_MAX_PROJECTION_STACK_DEPTH, &maxProjectionStacks);
            glGetIntegerv(GL_MAX_ATTRIB_STACK_DEPTH, &maxAttribStacks);
            glGetIntegerv(GL_MAX_TEXTURE_STACK_DEPTH, &maxTextureStacks);
    }

    string GraphicDeviceInfo::getAsString(){
        stringstream ss;
        ss << '\n';
        ss << "=============================OpenGL Info==============================" << '\n';
        ss << "Vendor: " << vendor << '\n';
        ss << "Version: " << version << '\n';
        ss << "Renderer: " << renderer << '\n';
        ss << "GlslVersion: " << glslVersion << '\n';
        ss << "Color Bits(R,G,B,A): (" << redBits << ", " << greenBits
            << ", " << blueBits << ", " << alphaBits << ")\n";
        ss << "Depth Bits: " << depthBits << '\n';
        ss << "Stencil Bits: " << stencilBits << '\n';
        ss << "Max Texture2D Size: " << maxTextureSize << "x" << maxTextureSize << '\n';
        ss << "Max Lights: " << maxLights << '\n';
        ss << "Max Clip Planes: " << maxClipPlanes << '\n';
        ss << "Max Modelview Matrix Stacks: " << maxModelViewStacks << '\n';
        ss << "Max Projection Matrix Stacks: " << maxProjectionStacks << '\n';
        ss << "Max Attribute Stacks: " << maxAttribStacks << '\n';
        ss << "Max Texture2D Stacks: " << maxTextureStacks << '\n';
        ss << "Total Number of Extensions: " << extensions.size() << '\n';
        for (unsigned int i = 0; i < extensions.size(); ++i)
            ss << extensions.at(i) << '\n';
        ss << "======================================================================" << '\n';
        return ss.str();
    }

    bool GraphicDeviceInfo::isExtensionSupported(const std::string & ext){
        std::vector<std::string>::const_iterator iter = extensions.begin();
        std::vector<std::string>::const_iterator endIter = extensions.end();
        while (iter != endIter) {
            if (ext == *iter)
                return true;
            else
                ++iter;
        }
        return false;
    }

    GraphicDevice::GraphicDevice(){
        glewInit();
        glEnable(GL_SCISSOR_TEST);
        glStencilMask(0xff);

        glDepthMask(true);
        glDepthFunc(GL_LESS);
        glEnable(GL_DEPTH_TEST);

        glDisable(GL_BLEND);
        glBlendFunc(GL_ONE_MINUS_SRC_ALPHA,GL_SRC_ALPHA);

        glFrontFace(GL_CCW);
        glCullFace(GL_BACK);
        glEnable(GL_CULL_FACE);
        glLineWidth(1.0);
        glPointSize(1.0);
        glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

        mGeometry=0;
        mGPUProgram=0;
        mRenderTarget=0;

        mParamCommitters[GPT_Int]=MAKE_PARAM_COMMITTER(commitParamInt);
        mParamCommitters[GPT_Float]=MAKE_PARAM_COMMITTER(commitParamFloat);
        mParamCommitters[GPT_Vec2]=MAKE_PARAM_COMMITTER(commitParamVec2);
        mParamCommitters[GPT_Vec3]=MAKE_PARAM_COMMITTER(commitParamVec3);
        mParamCommitters[GPT_Vec4]=MAKE_PARAM_COMMITTER(commitParamVec4);
        mParamCommitters[GPT_Mat2]=MAKE_PARAM_COMMITTER(commitParamMat2);
        mParamCommitters[GPT_Mat3]=MAKE_PARAM_COMMITTER(commitParamMat3);
        mParamCommitters[GPT_Mat4]=MAKE_PARAM_COMMITTER(commitParamMat4);
        mParamCommitters[GPT_Texture]=MAKE_PARAM_COMMITTER(commitParamInt);
    }

    void GraphicDevice::logDeviceInfo(){
        GraphicDeviceInfo info;
        LogManager::getSingletonPtr()->logMessage(info.getAsString());
    }

    void GraphicDevice::catchError(const string& loc){
        int ec=glGetError();
        if(ec!=GL_NO_ERROR){
            string err=(const char*)gluErrorString(ec);
            LogManager::getSingletonPtr()->logMessage(loc+":"+err);
        }
    }

    void GraphicDevice::setRastState(const RastState& _state){
        mRastState=_state;
        if(mRastState.mCull){
            glEnable(GL_CULL_FACE);
            glCullFace(mRastState.mCullMode);
            glFrontFace(mRastState.mFrontFace);
        }else{
            glDisable(GL_CULL_FACE);
        }
        glPolygonMode(GL_FRONT_AND_BACK,mRastState.mFillMode);
        if(mRastState.mFillMode==GL_LINE)
            glLineWidth(mRastState.mLineWidth);
        else if(mRastState.mFillMode==GL_POINT)
            glPointSize(mRastState.mPointSize);
        if(mRastState.mLineStipple){
            glEnable(GL_LINE_STIPPLE);
            glLineStipple(
                mRastState.mStippleRepeat,
                mRastState.mStipplePattern);
        }else{
            glDisable(GL_LINE_STIPPLE);
        }
    }

    void GraphicDevice::setBlendState(const BlendState& _state){
        mBlendState=_state;
        if(mBlendState.mBlend){
            glEnable(GL_BLEND);
            glBlendFunc(mBlendState.mSrcFactor,mBlendState.mDestFactor);
        }else{
            glDisable(GL_BLEND);
        }
    }

    void GraphicDevice::setDepthState(const DepthState& _state){
        mDepthState=_state;
        if(mDepthState.mTest){
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(mDepthState.mCompare);
        }else{
            glDisable(GL_DEPTH_TEST);
        }
        glDepthMask(mDepthState.mWrite);
    }

    void GraphicDevice::setStencilState(const StencilState& _state){
        mStencilState=_state;
        if(mStencilState.mTest){
            glEnable(GL_STENCIL_TEST);
            glStencilOp(
                mStencilState.mStencilFailOperator,
                mStencilState.mDepthFailOperator,
                mStencilState.mDepthSuccOperator);
            glStencilFunc(
                mStencilState.mStencilFunc,
                mStencilState.mReference,
                mStencilState.mMask);
        }else{
            glDisable(GL_STENCIL_TEST);
        }
    }

    void GraphicDevice::setSamplerState(const SamplerState& _state){
        mSamplerState=_state;
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,mSamplerState.mSWrap);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,mSamplerState.mTWrap);
    }

}